package com.lxw.multidb.modules.file;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

/**
 * 本地文件系统目录树服务
 * <p>
 * 核心功能：
 * - 加载指定路径下的直接子节点（一级目录 + 一级文件）
 * - 支持灵活的过滤和排序配置
 * - 使用try-with-resources确保资源释放
 * - 防止符号链接循环
 * <p>
 * 路径规范：
 * - TreeNode.fullPath存储相对路径（不包含basePath）
 * - 统一使用"/"作为路径分隔符
 * - 目录路径以"/"结尾：data/images/
 * - 文件路径不以"/"结尾：data/photo.jpg
 *
 * @author lxw
 * @version 3.3
 * @date 2025-11-06
 */
@Slf4j
@Service
public class LocalTreeService {

    private static final int MAX_FILES_PER_DIRECTORY = 10000;
    private static final String PATH_SEPARATOR = "/";

    /**
     * 加载指定路径下的子节点
     * <p>
     * 路径说明：
     * - basePath: 基础路径，如 /home/user 或 D:/workspace
     * - prefix: 相对路径，如 Documents 或 src/main/java
     * - TreeNode.fullPath: 相对路径（不含basePath）
     *
     * @param basePath 基础路径，如 /home/user 或 D:/workspace
     * @param prefix   子路径（可为null或空）
     * @param options  过滤和排序选项，不能为null
     * @return 子节点列表（已过滤、已排序）
     */
    public List<TreeNode> loadChildren(String basePath, String prefix, TreeOptions options) {
        log.info("加载子节点 - 基础路径: {}, 子路径: '{}', 配置: {}", basePath, prefix, options);

        // 验证配置
        options.validate();

        // 标准化路径
        basePath = TreeServiceUtils.normalizePath(basePath);
        prefix = TreeServiceUtils.normalizePath(prefix);

        // 验证目录
        File directory = validateDirectory(basePath, prefix);

        // 构建子节点列表
        List<TreeNode> children = buildChildren(directory, prefix, options);
        log.info("加载完成 - 路径: '{}', 子节点数: {} ", prefix, children.size());
        // debug日志输出目录和文件数量
        log.debug(" ({}个目录, {}个文件)",
                children.stream().filter(TreeNode::isDirectory).count(),
                children.stream().filter(node -> !node.isDirectory()).count());

        return children;
    }

    // ==================== 核心构建逻辑 ====================

    /**
     * 构建子节点列表（使用try-with-resources确保Stream关闭）
     */
    private List<TreeNode> buildChildren(File directory, String prefix, TreeOptions options) {
        List<TreeNode> children = new ArrayList<>();

        // 使用try-with-resources自动关闭Stream
        try (Stream<Path> paths = Files.list(directory.toPath())) {
            paths.limit(MAX_FILES_PER_DIRECTORY)
                    .forEach(path -> {
                        try {
                            File file = path.toFile();
                            TreeNode node = null;
                            if (file.isDirectory()) {
                                node = processDirectory(file, prefix, options);
                            } else if (file.isFile()) {
                                node = processFile(file, prefix, options);
                            }

                            if (node != null) {
                                children.add(node);
                            }
                        } catch (Exception e) {
                            log.error("处理文件时出错: {}", path, e);
                        }
                    });
        } catch (IOException e) {
            log.error("读取目录失败: {}", directory.getAbsolutePath(), e);
            return new ArrayList<>();
        }
        // 大量子节点预警
        if (children.size() >= MAX_FILES_PER_DIRECTORY) {
            log.warn("目录包含{}个子节点，已达到限制", children.size());
        }
        // 排序
        if (!children.isEmpty()) {
            TreeServiceUtils.sortChildren(children, options);
        }
        return children;
    }

    // ==================== 目录和文件处理 ====================

    /**
     * 处理目录节点
     */
    private TreeNode processDirectory(File directory, String prefix, TreeOptions options) {
        String dirName = directory.getName();
        log.debug("发现目录: {}", dirName);

        // 应用过滤
        if (TreeServiceUtils.shouldFilterDirectory(dirName, options)) {
            log.debug("目录被过滤: {}", dirName);
            return null;
        }
        String relativePath = TreeServiceUtils.buildRelativePath(prefix, dirName, true);
        return new TreeNode(dirName, relativePath, true);
    }

    /**
     * 处理文件节点
     */
    private TreeNode processFile(File file, String prefix, TreeOptions options) {
        String fileName = file.getName();
        log.debug("发现文件: {}, 大小: {} bytes", fileName, file.length());

        // 应用过滤
        if (TreeServiceUtils.shouldFilterFile(fileName, options)) {
            log.debug("文件被过滤: {}", fileName);
            return null;
        }

        return createFileNode(fileName, file, prefix);
    }

    /**
     * 创建文件节点并设置属性
     */
    private TreeNode createFileNode(String fileName, File file, String prefix) {
        String relativePath = TreeServiceUtils.buildRelativePath(prefix, fileName, false);
        TreeNode fileNode = new TreeNode(fileName, relativePath, false);

        fileNode.setSize(file.length());
        String extension = TreeNode.extractExtension(fileName);
        fileNode.setExtension(extension);

        return fileNode;
    }

    // ==================== 路径处理 ====================

    /**
     * 验证并获取目录
     */
    private File validateDirectory(String basePath, String prefix) {
        String fullPath = buildFullPath(basePath, prefix);
        File directory = new File(fullPath);

        if (!directory.exists() || !directory.isDirectory()) {
            log.error("无效的目录路径: {}", fullPath);
            throw new IllegalArgumentException("无效的目录路径: " + fullPath);
        }

        return directory;
    }

    /**
     * 构建完整的物理路径（用于访问文件系统）
     */
    private String buildFullPath(String basePath, String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            return basePath;
        }

        String separator = File.separator;

        // 确保basePath不以分隔符结尾
        if (basePath.endsWith(separator) || basePath.endsWith(PATH_SEPARATOR)) {
            basePath = basePath.substring(0, basePath.length() - 1);
        }

        // 确保prefix不以分隔符开头
        if (prefix.startsWith(separator) || prefix.startsWith(PATH_SEPARATOR)) {
            prefix = prefix.substring(1);
        }

        // 替换/为系统分隔符
        prefix = prefix.replace(PATH_SEPARATOR, separator);

        return basePath + separator + prefix;
    }
}